using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Sedna.Util.Enum;

namespace Sedna.Util
{
    /// <summary>
    /// This class allows to perform safe check if a string represents an integer or a fractional number
    /// using regular expressions.
    /// 
    /// Default decimal separator (delimiter) can be set. Its initial value is taken from 
    /// <see cref="Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator"/>.
    /// 
    /// This class doesn't take into accout number group separator.
    /// 
    /// Note that if you use a delimiter that contains a point then the class automatically substitute this working symbol.
    /// For other symbols reserved in regular expression language it is necessary to lead them by slash.
    /// 
    /// </summary>
    public class Numeric
    {
        public static String DefaultDecimalSeparator = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;

        private Numeric() { }

        # region Integer

        public static bool IsInteger(String value)
        {
            return IsInteger(value, NumberSign.Any);
        }

        public static bool IsInteger(String value, NumberSign sign)
        {
            return IsNumeric(value, "", sign);
        }

        # endregion

        # region Numeric

        public static bool IsNumeric(String value)
        {
            return IsNumeric(value, null, NumberSign.Any);
        }

        public static bool IsNumeric(String value, NumberSign sign)
        {
            return IsNumeric(value, null, sign);
        }

        public static bool IsNumeric(String value, String delimiter)
        {
            return IsNumeric(value, delimiter, NumberSign.Any);
        }

        public static bool IsNumeric(String value, String delimiter, NumberSign sign)
        {
            if (String.IsNullOrEmpty(value))
                return false;
            
            Match match = Regex.Match(value,
                                      String.Format(@"^{0}({1}\d+|\d+{1}\d+|\d+{1})$",
                                                    GetSignExpr(sign), GetDelimiterExpr(delimiter)));

            return (match.Success && match.Index == 0 && match.Length == value.Length);
        }

        # endregion

        # region Specific Types

        public static bool IsInt32(String value)
        {
            if (String.IsNullOrEmpty(value))
                return false;
            
            int result;

            return int.TryParse(value, out result);
        }

        public static bool IsInt32(String value, NumberSign sign)
        {
            return IsInt32(value) && IsInteger(value, sign);
        }

        public static bool IsInt64(String value)
        {
            if (String.IsNullOrEmpty(value))
                return false;
            
            long result;

            return long.TryParse(value, out result);
        }

        public static bool IsInt64(String value, NumberSign sign)
        {
            return IsInt64(value) && IsInteger(value, sign);
        }
        
        # endregion

        # region Helpers

        private static String GetSignExpr(NumberSign sign)
        {
            String signExpr;

            switch (sign)
            {
                case NumberSign.Any:
                    {
                        signExpr = @"((\+|-)?)";
                        break;
                    }
                case NumberSign.Positive:
                    {
                        signExpr = @"(\+?)";
                        break;
                    }
                case NumberSign.Negative:
                    {
                        signExpr = "(-)";
                        break;
                    }
                case NumberSign.Unsigned:
                    {
                        signExpr = "";
                        break;
                    }
                case NumberSign.Signed:
                    {
                        signExpr = @"(\+|-)";
                        break;
                    }
                default:
                    {
                        throw new ArgumentException(String.Format("Unknown number sign: '{0}'", sign));
                    }
            }

            return signExpr;
        }

        private static String GetDelimiterExpr(String delimiter)
        {
            if (delimiter == null)
            {
                delimiter = DefaultDecimalSeparator;
            }

            delimiter = delimiter.Replace(".", @"\.");

            return delimiter.Length == 0 ? String.Empty : String.Format(@"(({0})?)", delimiter);
        }

        # endregion
    }
}
